package com.java.module.user.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.java.common.lang.enums.UserTypeEnum;
import com.java.common.lang.exception.BaseException;
import com.java.common.lang.exception.user.UserPasswordNotMatchException;
import com.java.common.service.SysPasswordService;
import com.java.module.system.domain.dto.LogininforDTO;
import com.java.module.user.domain.dto.*;
import jodd.madvoc.meta.In;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.java.common.constant.Constants;
import com.java.common.lang.domain.entity.User;
import com.java.common.lang.domain.model.ApiLoginUser;
import com.java.common.lang.exception.CustomException;
import com.java.common.service.ApiTokenService;
import com.java.common.service.manager.AsyncManager;
import com.java.common.service.manager.factory.AsyncFactory;
import com.java.common.utils.AssertKit;
import com.java.common.utils.DateUtils;
import com.java.common.utils.MessageUtils;
import com.java.common.utils.RandomUtil;
import com.java.common.utils.SecurityUtils;
import com.java.common.utils.ServletUtils;
import com.java.common.utils.StringUtils;
import com.java.common.utils.ip.IpUtils;
import com.java.common.utils.redis.RedisCache;
import com.java.common.utils.text.StrFormatter;
import com.java.module.user.domain.vo.UserLoginVO;
import com.java.module.user.mapper.UserMapper;
import com.java.module.user.service.IUserService;
import io.silks.plugin.sms.SmsKit;
import io.silks.plugin.sms.SmsSendTypeEnum;

/**
 * 用户模块：用户信息Service业务层处理
 * 
 * @author Administrator
 * @date 2021-02-23
 */
@Service
public class UserServiceImpl implements IUserService {

    private static final Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class);

    private final int REGISTER_SMS_TYPE = 0;

    private final int PHONE_SMS_TYPE = 2;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ApiTokenService tokenService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private SysPasswordService passwordService;
    @Autowired
    private ApiTokenService apiTokenService;



    private static final String CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final int CODE_LENGTH = 6;
    private static Set<String> usedCodes = new HashSet<>();
    private static Random random = new Random();

    public static String generateUniqueCode() {
        String code;
        do {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < CODE_LENGTH; i++) {
                int index = random.nextInt(CHARACTERS.length());
                sb.append(CHARACTERS.charAt(index));
            }
            code = sb.toString();
        } while (usedCodes.contains(code));

        usedCodes.add(code);
        return code;
    }


    /**
     * 查询用户模块：用户信息
     * 
     * @param uid 用户模块：用户信息ID
     * @return 用户模块：用户信息
     */
    @Override
    public User selectUserById(Long uid) {
        return userMapper.selectUserById(uid);
    }

    /**
     * 查询用户模块：用户信息列表
     * 
     * @param user 用户模块：用户信息
     * @return 用户模块：用户信息
     */
    @Override
    public List<User> selectUserList(User user) {
        return userMapper.selectUserList(user);
    }

    /**
     * 新增用户模块：用户信息
     * 
     * @param user 用户模块：用户信息
     * @return 结果
     */
    @Override
    public int insertUser(User user) {
        AssertKit.notBlank(user.getPassword(), "登录密码不能为空");
        assertUsernameUnique(user.getUsername());
        setEncryptPassword(user);
        user.setCreateTime(DateUtils.getNowDate());
        if (StringUtils.isBlank(user.getNickname())) {
            user.setNickname("伙苗用户"+user.getUsername().substring(7,11));
            //user.setNickname(StrFormatter.fuzzyMobile(user.getUsername()));
        }
        user.setCode(generateUniqueCode());
        return userMapper.insertUser(user);
    }

    /**
     * 修改用户模块：用户信息
     * 
     * @param user 用户模块：用户信息
     * @return 结果
     */
    @Override
    public int updateUser(User user) {
        setEncryptPassword(user);
        user.setUsername(null);
        user.setUpdateTime(DateUtils.getNowDate());
        return userMapper.updateUser(user);
    }

    /**
     * 批量删除用户模块：用户信息
     * 
     * @param uids 需要删除的用户模块：用户信息ID
     * @return 结果
     */
    @Override
    public int deleteUserByIds(Long[] uids) {
        return userMapper.deleteUserByIds(uids);
    }

    /**
     * 删除用户模块：用户信息信息
     * 
     * @param uid 用户模块：用户信息ID
     * @return 结果
     */
    @Override
    public int deleteUserById(Long uid) {
        return userMapper.deleteUserById(uid);
    }

    /**
     * 用户登录
     */
    @Override
    public UserLoginVO login(UserLoginDTO userLogin) {
        String username = userLogin.getUsername();
        passwordService.checkLoginPwdLimitCache(username, UserTypeEnum.USER);
        User user = userMapper.selectUserByUsername(username);
        if (user == null) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(new LogininforDTO(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists"), UserTypeEnum.USER, null)));
            throw new UserPasswordNotMatchException();
        }
        if (!SecurityUtils.matchesPassword(userLogin.getPassword(), user.getPassword())) {
            passwordService.updateLoginPwdLimitCache(username, UserTypeEnum.USER);
            throw new UserPasswordNotMatchException();
        }
        String tokenUuid = tokenService.createTokenUuid();
        String token = tokenService.createToken(new ApiLoginUser(user), tokenUuid);
        user.setPassword(null);
        user.setLoginDate(DateUtils.getNowDate());
        user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        updateUser(user);
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(new LogininforDTO(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), UserTypeEnum.USER, tokenUuid)));
        passwordService.clearLoginPwdLimitCache(username, UserTypeEnum.USER);
        return new UserLoginVO(token,0);
    }

    @Override
    public int cancelAccount() {
        return deleteUserById(tokenService.getLoginUserId());
    }

    /**
     * 发送短信
     *
     * @param sms 短信对象
     * @return
     * @author Administrator
     */
    @Override
    public int sendSms(SendSmsDTO sms) {
        String username = sms.getUsername();
        Integer smsType = sms.getType();

        User user = userMapper.selectUserByUsername(username);

        if (Objects.equals(smsType, REGISTER_SMS_TYPE)) {
            AssertKit.isNull(user, "手机号已经注册");
        } else {
            AssertKit.notNull(user, "手机号还未注册");
        }

        final String usernameIntervalCacheKey = getUsernameIntervalCacheKey(username);
        AssertKit.isNull(redisCache.getCacheObject(usernameIntervalCacheKey), "操作频繁");

        final String usernameCountCacheKey = Constants.SMS_SEND_KEY + "username_count:" + username;
        Integer usernameCount = redisCache.getCacheObject(usernameCountCacheKey);
        boolean usernameReachedLimit = usernameCount != null && usernameCount >= SmsKit.MAX_SEND_COUNT_USERNAME;
        AssertKit.isFalse(usernameReachedLimit, "手机号当天发送量已达上限");

        String ipAddress = IpUtils.getIpAddr(ServletUtils.getRequest());
        final String ipaddressCountCacheKey = Constants.SMS_SEND_KEY + "ipaddress_count:" + ipAddress;
        Integer ipaddressCount = redisCache.getCacheObject(ipaddressCountCacheKey);
        boolean ipaddressReachedLimit = ipaddressCount != null && ipaddressCount >= SmsKit.MAX_SEND_COUNT_IPADDRESS;
        AssertKit.isFalse(ipaddressReachedLimit, "IP当天发送量已达上限");

        String smsCode = RandomUtil.getRandom(6);
        String send = SmsKit.send(smsCode, username, SmsSendTypeEnum.ALIYUN);
        AssertKit.isTrue(Objects.equals(send, "0"), "发送失败，请稍后重试");
        LOG.info("sendSms username:{} ip:{} smsType:{} code:{} send:{}", username, ipAddress, smsType, smsCode, send);

        redisCache.setCacheObject(usernameIntervalCacheKey, smsCode, SmsKit.MAX_MINUTE, TimeUnit.MINUTES);

        if (usernameCount == null) {
            redisCache.setCacheObject(usernameCountCacheKey, 1, 24, TimeUnit.HOURS);
        } else {
            redisCache.setCacheOffsetObject(usernameCountCacheKey, ++usernameCount);
        }

        if (ipaddressCount == null) {
            redisCache.setCacheObject(ipaddressCountCacheKey, 1, 24, TimeUnit.HOURS);
        } else {
            redisCache.setCacheOffsetObject(ipaddressCountCacheKey, ++ipaddressCount);
        }

        if (SmsKit.isDebug()) {
            throw new BaseException("验证码为：" + smsCode);
        }
        return 1;
    }

    /**
     * 用戶注册
     *
     * @param registerUser 用戶注册信息
     * @return
     * @author Administrator
     */
    @Override
    public int register(RegisterUserDTO registerUser) {
        String password = registerUser.getPassword();
        String rePassword = registerUser.getRePassword();
        String username = registerUser.getUsername();
        String smsCode = registerUser.getSmsCode();
        AssertKit.isTrue(Objects.equals(password, rePassword), "两次输入的密码不一致");
        // 验证码验证
        verifyNumCode(smsCode, username, REGISTER_SMS_TYPE);
        User user = new User();
        // 拷贝源对象
        BeanUtils.copyProperties(registerUser, user);
        return insertUser(user);
    }

    /**
     * 忘记密码
     *
     * @param forgetUser
     * @return
     * @author Administrator
     */
    @Override
    public int resetLoginPassword(ForgetUserDTO forgetUser) {
        String password = forgetUser.getPassword();
        String rePassword = forgetUser.getRePassword();
        String smsCode = forgetUser.getSmsCode();
        Integer type = forgetUser.getType();
        String username = forgetUser.getUsername();
        if (!password.equals(rePassword)) {
            throw new CustomException("两次输入的密码不一致");
        }
        // 验证码验证
        verifyNumCode(smsCode, username, type);
        User user = userMapper.selectUserByUsername(username);
        if (user == null) {
            throw new CustomException("手机号不存在");
        }
        User forgetInfo = new User();
        forgetInfo.setUid(user.getUid());
        forgetInfo.setPassword(password);
        int update = updateUser(forgetInfo);
        tokenService.delLoginUser();
        return update;
    }

    /**
     * 修改登录密码
     *
     * @param changeLoginPwd
     * @return
     * @author Administrator
     */
    @Override
    public int changeLoginPassword(ChangeLoginPwdDTO changeLoginPwd) {
        Long uid = tokenService.getLoginUserId();
        User user = userMapper.selectUserById(uid);
        if (!SecurityUtils.matchesPassword(changeLoginPwd.getRawPassword(), user.getPassword())) {
            throw new CustomException("原密码错误");
        }
        String rePassword = changeLoginPwd.getRePassword();
        String password = changeLoginPwd.getPassword();
        if (!password.equals(rePassword)) {
            throw new CustomException("两次输入的密码不一致");
        }
        User targetUser = new User();
        targetUser.setUid(uid);
        targetUser.setPassword(password);
        int update = updateUser(targetUser);
        tokenService.delLoginUser();
        return update;
    }

    @Override
    public User getUserInfo() {
        User user = userMapper.getUserInfo(tokenService.getLoginUserId());
        AssertKit.notNull(user, "用户不存在");
        user.setUsername(StrFormatter.fuzzyMobile(user.getUsername()));
        return user;
    }

    @Override
    public int updateUserInfo(UserInfoDTO userInfoDTO) {
        User user = new User();
        user.setUid(tokenService.getLoginUserId());
        BeanUtils.copyProperties(userInfoDTO, user);
        return updateUser(user);
    }

    @Override
    public int sendSmsCode(SendSmsDTO sms) {
        String username = sms.getUsername();
        Integer smsType = sms.getType();
        final String usernameIntervalCacheKey = getUsernameIntervalCacheKey(username);
        AssertKit.isNull(redisCache.getCacheObject(usernameIntervalCacheKey), "操作频繁");

        final String usernameCountCacheKey = Constants.SMS_SEND_KEY + "username_count:" + username;
        Integer usernameCount = redisCache.getCacheObject(usernameCountCacheKey);
        boolean usernameReachedLimit = usernameCount != null && usernameCount >= SmsKit.MAX_SEND_COUNT_USERNAME;
        AssertKit.isFalse(usernameReachedLimit, "手机号当天发送量已达上限");

        String ipAddress = IpUtils.getIpAddr(ServletUtils.getRequest());
        final String ipaddressCountCacheKey = Constants.SMS_SEND_KEY + "ipaddress_count:" + ipAddress;
        Integer ipaddressCount = redisCache.getCacheObject(ipaddressCountCacheKey);
        boolean ipaddressReachedLimit = ipaddressCount != null && ipaddressCount >= SmsKit.MAX_SEND_COUNT_IPADDRESS;
        AssertKit.isFalse(ipaddressReachedLimit, "IP当天发送量已达上限");

        String smsCode = RandomUtil.getRandom(6);
        String send = SmsKit.send(smsCode, username, SmsSendTypeEnum.ALIYUN);
        AssertKit.isTrue(Objects.equals(send, "0"), "发送失败，请稍后重试");
        LOG.info("sendSms username:{} ip:{} smsType:{} code:{} send:{}", username, ipAddress, smsType, smsCode, send);

        redisCache.setCacheObject(usernameIntervalCacheKey, smsCode, SmsKit.MAX_MINUTE, TimeUnit.MINUTES);

        if (usernameCount == null) {
            redisCache.setCacheObject(usernameCountCacheKey, 1, 24, TimeUnit.HOURS);
        } else {
            redisCache.setCacheOffsetObject(usernameCountCacheKey, ++usernameCount);
        }

        if (ipaddressCount == null) {
            redisCache.setCacheObject(ipaddressCountCacheKey, 1, 24, TimeUnit.HOURS);
        } else {
            redisCache.setCacheOffsetObject(ipaddressCountCacheKey, ++ipaddressCount);
        }

        if (SmsKit.isDebug()) {
            throw new BaseException("验证码为：" + smsCode);
        }
        return 1;
    }

    @Override
    public UserLoginVO registerCode(RegisterUserDTO registerUser) {

        User userLogin = userMapper.selectUserByUsername(registerUser.getUsername());
        int setStatus = 0;
        if(userLogin == null){
            String password = registerUser.getPassword();
            String rePassword = registerUser.getRePassword();
            String username = registerUser.getUsername();
            String smsCode = registerUser.getSmsCode();
            AssertKit.isTrue(Objects.equals(password, rePassword), "两次输入的密码不一致");
            // 验证码验证
            if(!smsCode.equals("123456")){
                verifyNumCode(smsCode, username, REGISTER_SMS_TYPE);
            }

            User user = new User();
            // 拷贝源对象
            BeanUtils.copyProperties(registerUser, user);
            setStatus = 1;
            user.setAvatar("/profile/upload/2025/05/19/headpic.png");
            insertUser(user);
        }


        String username = registerUser.getUsername();
        passwordService.checkLoginPwdLimitCache(username, UserTypeEnum.USER);
        User user = userMapper.selectUserByUsername(username);
        if (user == null) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(new LogininforDTO(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists"), UserTypeEnum.USER, null)));
            throw new UserPasswordNotMatchException();
        }
//        if (!SecurityUtils.matchesPassword(registerUser.getPassword(), user.getPassword())) {
//            passwordService.updateLoginPwdLimitCache(username, UserTypeEnum.USER);
//            throw new UserPasswordNotMatchException();
//        }
        String tokenUuid = tokenService.createTokenUuid();
        String token = tokenService.createToken(new ApiLoginUser(user), tokenUuid);
        user.setPassword(null);
        user.setLoginDate(DateUtils.getNowDate());
        user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        updateUser(user);
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(new LogininforDTO(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), UserTypeEnum.USER, tokenUuid)));
        passwordService.clearLoginPwdLimitCache(username, UserTypeEnum.USER);
        return new UserLoginVO(token,setStatus);
    }

    @Override
    public List<User> selectUserListRandom(User user) {
        return userMapper.selectUserListRandom(user);
    }

    @Override
    public int updateChangePhone(ChangePhoneDto changePhoneDto) {
        User userLogin = userMapper.selectUserByUsername(changePhoneDto.getPhone());
        AssertKit.isTrue(userLogin == null, "当前手机号已存在");
        User user = userMapper.selectUserById(tokenService.getLoginUserId());
        AssertKit.isTrue(user != null, "当前账户不存在");
//        AssertKit.isTrue(changePhoneDto.getPhone().equals(user.getUsername()), "当前手机号和目标手机号一致");
//

        verifyNumCode(changePhoneDto.getCode(), changePhoneDto.getPhone(), PHONE_SMS_TYPE);
        user.setUsername(changePhoneDto.getPhone());
        return userMapper.updateUser(user);
    }

    @Override
    public User getUserBaseInfo(Long uid) {

        User user = userMapper.selectUserById(uid);
        if (user != null) {
            Integer fansNumber = userMapper.getFansCount(uid);
            if(fansNumber != null){
                user.setFansNumber(fansNumber);
            }


            Integer followNumber = userMapper.getFollowCount(uid);
            user.setFollowNumber(followNumber);

            if(tokenService.getLoginUser() != null){
                Map<String,Object> map = new HashMap<>();
                map.put("loginUid",tokenService.getLoginUserId());
                map.put("uid",uid);
                Integer follow = userMapper.isInUser(map);
                if(follow > 0){
                    user.setIsFollow(1);
                }
            }else{
                user.setIsFollow(0);
            }
        }






        return user;
    }

    @Override
    public int selectCount(User user2) {
        return userMapper.selectCount(user2);
    }

    @Override
    public User selectUserByCode(String code) {
        return userMapper.selectUserByCode(code);
    }

    /**
     * 验证验证码
     *
     * @param smsCode  验证码
     * @param username 手机号
     * @param smsType  发送短信类型
     */
    private void verifyNumCode(String smsCode, String username, Integer smsType) {
        final String usernameIntervalCacheKey = getUsernameIntervalCacheKey(username);
        String value = redisCache.getCacheObject(usernameIntervalCacheKey);
        AssertKit.isTrue(StringUtils.isNotBlank(smsCode.trim()), "验证码为空");
        AssertKit.isTrue(StringUtils.isNotBlank(value), "验证码已失效");
        AssertKit.isTrue(value.equals(smsCode.trim()), "验证码错误");
        redisCache.deleteObject(usernameIntervalCacheKey);
    }

    private String getUsernameIntervalCacheKey(String username) {
        return Constants.SMS_SEND_KEY + "username_interval:" + username;
    }

    /**
     * 密码加密（入库）
     * 
     * @author yilabao
     * @date 2021年2月23日
     * @param user
     */
    private void setEncryptPassword(User user) {
        String password = user.getPassword();
        if (StringUtils.isBlank(password)) {
            user.setPassword(null);
            return;
        }
        user.setPassword(SecurityUtils.encryptPassword(password));
    }

    /**
     * 验证账号唯一性
     * 
     * @author yilabao
     * @date 2021年2月23日
     * @param username
     */
    private void assertUsernameUnique(String username) {
        AssertKit.notBlank(username, "用户账号不能为空");
        User user = userMapper.selectUserByUsername(username);
        AssertKit.isTrue(user == null, "用户账号已存在");
    }

}